package com.tgh.utils;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLES11Ext;
import android.opengl.GLES20;
import android.opengl.GLUtils;
import android.opengl.Matrix;
import android.text.TextUtils;
import android.util.Log;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.ShortBuffer;
import java.util.Arrays;
import java.util.List;

import static android.opengl.GLES20.*;


public class GLTools {

    private GLTools() {}
    
    
    public static ByteBuffer makeByteBuffer(byte[] array) {
        ByteBuffer vbb=ByteBuffer.allocateDirect(array.length);
        vbb.order(ByteOrder.nativeOrder());
        vbb.put(array);
        vbb.position(0);
        return vbb;
    }
    
    public static ByteBuffer makeByteBuffer(byte[][] inArray) {
        byte[] array = twoDArray2OneDArray(inArray);
        return makeByteBuffer(array);
    }
    
    public static ShortBuffer makeShortBuffer(short[] array) {
        ByteBuffer vbb=ByteBuffer.allocateDirect(array.length*2);
        vbb.order(ByteOrder.nativeOrder());
        ShortBuffer buffer = vbb.asShortBuffer();
        buffer.put(array);
        buffer.position(0);
        return buffer;
    }
    
    public static ShortBuffer makeShortBuffer(short[][] inArray) {
    	short[] array = twoDArray2OneDArray(inArray);
        return makeShortBuffer(array);
    }
    
    public static IntBuffer makeIntBuffer(int[] array) {
        ByteBuffer vbb=ByteBuffer.allocateDirect(array.length*4);
        vbb.order(ByteOrder.nativeOrder());
        IntBuffer buffer = vbb.asIntBuffer();
        buffer.put(array);
        buffer.position(0);
        return buffer;
    }
    
    public static IntBuffer makeIntBuffer(int[][] inArray) {
        int[] array = twoDArray2OneDArray(inArray);
        return makeIntBuffer(array);
    }
    public static FloatBuffer makeFloatBuffer(float[] array) {
        ByteBuffer vbb=ByteBuffer.allocateDirect(array.length*4);
        vbb.order(ByteOrder.nativeOrder());
        FloatBuffer buffer = vbb.asFloatBuffer();
        buffer.put(array);
        buffer.position(0);
        return buffer;
    }
    public static FloatBuffer makeFloatBuffer(float[][] inArray) {
        float[] array = twoDArray2OneDArray(inArray);
        return makeFloatBuffer(array);
    }
    
    public static void setPerspective(float[] m,float fov,float aspect,float near,float far) {
        float xmin,xmax,ymin,ymax;
        
        ymax=(float) (near*Math.tan(fov*Math.PI/360f));
        ymin=-ymax;
        xmin=ymin*aspect;
        xmax=-xmin;
        Matrix.frustumM(m, 0, xmin, xmax, ymin, ymax, near, far);
    }
    

    public static int[] twoDArray2OneDArray(int[][] twoDArray) {
        int length=0;
        for(int i=0;i<twoDArray.length;i++){
            length+=twoDArray[i].length;
        }
        int[] oneDArray=new int[length];
        for(int i=0;i<twoDArray.length;i++){
            for(int j=0;j<twoDArray[i].length;j++){
                oneDArray[i*twoDArray[i].length+j]=twoDArray[i][j];
            }
        }
        return oneDArray;
    }
    
    public static short[] twoDArray2OneDArray(short[][] twoDArray) {
    	int length=0;
    	for(int i=0;i<twoDArray.length;i++){
    		length+=twoDArray[i].length;
    	}
    	short[] oneDArray=new short[length];
    	for(int i=0;i<twoDArray.length;i++){
    		for(int j=0;j<twoDArray[i].length;j++){
    			oneDArray[i*twoDArray[i].length+j]=twoDArray[i][j];
    		}
    	}
    	return oneDArray;
    }
    
    public static float[] twoDArray2OneDArray(float[][] twoDArray) {
        int length=0;
        for(int i=0;i<twoDArray.length;i++){
            length+=twoDArray[i].length;
        }
        float[] oneDArray=new float[length];
        length=0;
        for(int i=0;i<twoDArray.length;i++){
            float[] array=twoDArray[i];
            for(int j=0 ; j<array.length; j++ ){
                oneDArray[length++]=twoDArray[i][j];
            }
        }
        return oneDArray;
    }
    
    public static byte[] twoDArray2OneDArray(byte[][] twoDArray) {
        int length=0;
        for(int i=0;i<twoDArray.length;i++){
            length+=twoDArray[i].length;
        }
        byte[] oneDArray=new byte[length];
        for(int i=0;i<twoDArray.length;i++){
            for(int j=0;j<twoDArray[i].length;j++){
                oneDArray[i*twoDArray[i].length+j]=twoDArray[i][j];
            }
        }
        return oneDArray;
    }
    
    public static float[] generateNormalsForTriangleList(float[] vertexs,byte[] indices) {
    	final int vertexCount=vertexs.length/3;
    	Vector3f[] normalsBuffer=new Vector3f[vertexCount];
    	final int triangleCount=indices.length/3;
    	Vector3f v1=new Vector3f();
    	Vector3f v2=new Vector3f();
    	Vector3f v3=new Vector3f();
    	Vector3f v1v2=new Vector3f();
    	Vector3f v1v3=new Vector3f();
    	Vector3f crossProduct=new Vector3f();
    	
    	for(int triangle=0;triangle<triangleCount;triangle++){
    		byte firstVertexIndex=indices[triangle*3];
    		byte secondVertexIndex=indices[triangle*3+1];
    		byte thirdVertexIndex=indices[triangle*3+2];
    		v1.copy(vertexs, firstVertexIndex*3);
    		v2.copy(vertexs, secondVertexIndex*3);
    		v3.copy(vertexs, thirdVertexIndex*3);
    		Vector3f.reduce(v1v2,v2, v1);
    		Vector3f.reduce(v1v3,v3, v1);
    		Vector3f.crossProduct(crossProduct,v1v2, v1v3);
    		crossProduct.normalize();
    		addVertexWithNormal(normalsBuffer,firstVertexIndex, crossProduct);
    		addVertexWithNormal(normalsBuffer, secondVertexIndex, crossProduct);
    		addVertexWithNormal(normalsBuffer, thirdVertexIndex, crossProduct);
    	}
    	for(int i=0;i<vertexCount;i++){
    		normalsBuffer[i].normalize();
    	}
    	
    	float[] normals=new float[vertexCount*3];
    	for(int i=0;i<vertexCount;i++){
    		v1=normalsBuffer[i];
    		normals[i*3]=v1.x;
    		normals[i*3+1]=v1.y;
    		normals[i*3+2]=v1.z;
    	}
    	return normals;
    }
    
    public static float[] generateNormalsForTriangleList(float[][] vertexs,byte[][] indices) {
        final int vertexCount=vertexs.length;
        Vector3f[] normalsBuffer=new Vector3f[vertexCount];
        final int triangleCount=indices.length;
        Vector3f v1=new Vector3f();
    	Vector3f v2=new Vector3f();
    	Vector3f v3=new Vector3f();
    	Vector3f v1v2=new Vector3f();
    	Vector3f v1v3=new Vector3f();
    	Vector3f crossProduct=new Vector3f();
    	
        for(int triangle=0;triangle<triangleCount;triangle++){
            byte[] triangleVertexIndex=indices[triangle];
            v1.copy(vertexs[triangleVertexIndex[0]], 0);
            v2.copy(vertexs[triangleVertexIndex[1]], 0);
            v3.copy(vertexs[triangleVertexIndex[2]], 0);
            Vector3f.reduce(v1v2, v2, v1);
            Vector3f.reduce(v1v3, v3, v1);
            Vector3f.crossProduct(crossProduct, v1v2, v1v3);
            addVertexWithNormal(normalsBuffer, triangleVertexIndex[0], crossProduct);
            addVertexWithNormal(normalsBuffer, triangleVertexIndex[1], crossProduct);
            addVertexWithNormal(normalsBuffer, triangleVertexIndex[2], crossProduct);
        }
        for(int i=0;i<vertexCount;i++){
            normalsBuffer[i].normalize();
        }
        
        float[] normals=new float[vertexCount*3];
        for(int i=0;i<vertexCount;i++){
            v1=normalsBuffer[i];
            normals[i*3]=v1.x;
            normals[i*3+1]=v1.y;
            normals[i*3+2]=v1.z;
        }
        return normals;
    }
    
    private static void addVertexWithNormal(Vector3f[] vectorBuffer,int vectorIndex,Vector3f normal) {
        Vector3f vector = vectorBuffer[vectorIndex];
        if (vector==null) {
            vector=new Vector3f(0, 0, 0);
        }
        Vector3f.add(vector, vector, normal);
        vectorBuffer[vectorIndex]=vector;
    }
    
    public static GLTriangleBatch makeSphere(float radius, int slices, int stacks){
    	GLTriangleBatch sphereBatch=new GLTriangleBatch();
    	final float drho=(float) (Math.PI/stacks);
    	final float dtheta=(float) (2.0f*Math.PI/slices);
    	final float ds=1.0f/slices;
    	final float dt=1.0f/stacks;
    	float t=1.0f;
    	float s=0.0f;
    	
    	Vector3f v1,v2,v3,v4;
    	Vector3f n1,n2,n3,n4;
    	Vector2f t1,t2,t3,t4;
    	for(int i=0;i<stacks;i++){
    		float rho=i*drho;
    		float srho=(float) Math.sin(rho);
    		float crho=(float) Math.cos(rho);
    		float srhodrho=(float) Math.sin(rho+drho);
    		float crhodrho=(float) Math.cos(rho+drho);
    		s=0.0f;
    		for(int j=0;j<slices;j++){
    			float theta=((j==slices)?0.0f:j*dtheta);
    			float stheta=(float) -Math.sin(theta);
    			float ctheta=(float) Math.cos(theta);
    			
    			float x=stheta*srho;
    			float y=ctheta*srho;
    			float z=crho;
    			
    			v1=new Vector3f(x*radius, y*radius, z*radius);
    			n1=new Vector3f(x, y, z);
    			t1=new Vector2f(s, t);
    			
    			x=stheta*srhodrho;
    			y=ctheta*srhodrho;
    			z=crhodrho;
    			
    			v2=new Vector3f(x*radius, y*radius, z*radius);
    			n2=new Vector3f(x, y, z);
    			t2=new Vector2f(s, t-dt);
    			
    			theta=((j+1)==slices? 0.0f:(j+1)*dtheta);
    			stheta=(float) -Math.sin(theta);
    			ctheta=(float) Math.cos(theta);
    			
    			x=stheta*srho;
    			y=ctheta*srho;
    			z=crho;
    			s+=ds;
    			
    			v3=new Vector3f(x*radius, y*radius, z*radius);
    			n3=new Vector3f(x, y, z);
    			t3=new Vector2f(s, t);
    			
    			x=stheta*srhodrho;
    			y=ctheta*srhodrho;
    			z=crhodrho;
    			
    			v4=new Vector3f(x*radius, y*radius, z*radius);
    			n4=new Vector3f(x, y, z);
    			t4=new Vector2f(s, t-dt);
    			
    			sphereBatch.addTriangle(new Vector3f[]{v1,v2,v3}, new Vector3f[]{n1,n2,n3}, new Vector2f[]{t1,t2,t3});
    			sphereBatch.addTriangle(new Vector3f[]{v3,v2,v4}, new Vector3f[]{n3,n2,n4}, new Vector2f[]{t3,t2,t4});
    		}
    		t-=dt;
    	}
    	sphereBatch.end();
    	return sphereBatch;
	}
    
    public static GLTriangleBatch makeTorus(float majorRadius,float minorRadius,int numMajor,int numMinor) {
    	float majorStep = (float) (2.0f*Math.PI / numMajor);
    	float minorStep = (float) (2.0f*Math.PI / numMinor);
    	int i, j;
    	GLTriangleBatch triangleBatch=new GLTriangleBatch();
    	Vector3f v1,v2,v3,v4;
    	Vector3f n1,n2,n3,n4;
    	Vector2f t1,t2,t3,t4;
    	for (i=0; i<numMajor; ++i) 
    	{
    		float a0 = i * majorStep;
    		float a1 = a0 + majorStep;
    		float x0 =(float) Math.cos(a0);
    		float y0 =(float) Math.sin(a0);
    		float x1 =(float) Math.cos(a1);
    		float y1 =(float) Math.sin(a1);
    		
    		
    		for (j=0; j<=numMinor; ++j) 
    		{
    			float b = j * minorStep;
    			float c = (float) Math.cos(b);
    			float r = minorRadius * c + majorRadius;
    			float z = minorRadius * (float) Math.sin(b);
    			
    			
    			
    			v1=new Vector3f(x0 * r,y0 * r,z);
    			n1=new Vector3f(x0*c, y0*c, z/minorRadius);
    			t1=new Vector2f((float)i/(float)numMajor,(float)j/(float)numMinor);
    			
    			v2=new Vector3f(x1 * r,y1 * r,z);
    			n2=new Vector3f(x1*c, y1*c, z/minorRadius);
    			t2=new Vector2f((float)(i+1)/(float)numMajor,(float)j/(float)numMinor);
    			// Next one over
    			b = (j+1) * minorStep;
    			c = (float) Math.cos(b);
    			r = minorRadius * c + majorRadius;
    			z = minorRadius * (float) Math.sin(b);
    			
    			
    			v3=new Vector3f(x0 * r,y0 * r,z);
    			n3=new Vector3f(x0*c, y0*c, z/minorRadius);
    			t3=new Vector2f((float)i/(float)numMajor,(float)(j+1)/(float)numMinor);
    			triangleBatch.addTriangle(new Vector3f[]{v1,v2,v3}, new Vector3f[]{n1,n2,n3}, new Vector2f[]{t1,t2,t3});
    			
    			v4=new Vector3f(x1 * r,y1 * r,z);
    			n4=new Vector3f(x1*c, y1*c, z/minorRadius);
    			t4=new Vector2f((float)(i+1)/(float)numMajor,(float)(j+1)/(float)numMinor);
    			triangleBatch.addTriangle(new Vector3f[]{v3,v2,v4}, new Vector3f[]{n3,n2,n4}, new Vector2f[]{t3,t2,t4});
    		}
    	}
    	triangleBatch.end();
    	return triangleBatch;
    }
    
    @SuppressLint("FloatMath")
    public static GLTriangleBatch makeDisk(float innerRadius, float outerRadius, int nSlices,int nStacks) {
        // How much to step out each stack
        float stepSizeRadial = outerRadius - innerRadius;
        if (stepSizeRadial < 0.0f){
            stepSizeRadial *= -1.0f;
        }
        stepSizeRadial /= (float) nStacks;

        float stepSizeSlice = (3.1415926536f * 2.0f) / (float) nSlices;

        GLTriangleBatch diskBatch = new GLTriangleBatch();

        Vector3f v1, v2, v3, v4;
        Vector3f n1, n2, n3, n4;
        Vector2f t1, t2, t3, t4;

        float radialScale = 1.0f / outerRadius;

        for (int i = 0; i < nStacks; i++) // Stacks
        {
            float theyta;
            float theytaNext;
            for (int j = 0; j < nSlices; j++) // Slices
            {
                float inner = innerRadius + (float) i * stepSizeRadial;
                float outer = innerRadius + (float) (i + 1) * stepSizeRadial;

                theyta = stepSizeSlice * (float) j;
                if (j == (nSlices - 1)) {
                    theytaNext = 0.0f;
                } else {
                    theytaNext = stepSizeSlice * (float) (j + 1);
                }
                float stheyta = (float) Math.sin(theyta);
                float ctheyta = (float) Math.cos(theyta);
                float stheytaNext = (float) Math.sin(theytaNext);
                float ctheytaNext = (float) Math.cos(theytaNext);
                // Inner First
                v1 = new Vector3f(ctheyta * inner, stheyta * inner, 0.0f);
                n1 = new Vector3f(0, 0, 1);
                t1 = new Vector2f((v1.x * radialScale + 1.0f) * 0.5f,(v1.y * radialScale + 1.0f) * 0.5f);

                // Outer First
                v2 = new Vector3f(ctheyta * outer, stheyta * outer, 0.0f);
                n2 = new Vector3f(0, 0, 1);
                t2 = new Vector2f((v2.x * radialScale + 1.0f) * 0.5f, (v2.y * radialScale + 1.0f) * 0.5f);

                // Inner Second
                v3 = new Vector3f(ctheytaNext * inner, stheytaNext * inner, 0.0f);
                n3 = new Vector3f(0, 0, 1);
                t3 = new Vector2f((v3.x * radialScale + 1.0f) * 0.5f,(v3.y * radialScale + 1.0f) * 0.5f);

                // Outer Second
                v4 = new Vector3f(ctheytaNext * outer, stheytaNext * outer, 0.0f);
                n4 = new Vector3f(0, 0, 1);
                t4 = new Vector2f((v3.x * radialScale + 1.0f) * 0.5f,  (v3.y * radialScale + 1.0f) * 0.5f);

                diskBatch.addTriangle(new Vector3f[] { v1, v2, v3}, new Vector3f[] {n1, n2, n3}, new Vector2f[] {t1, t2, t3});
                diskBatch.addTriangle(new Vector3f[] {v3, v2, v4}, new Vector3f[] { n3, n2, n4}, new Vector2f[] { t3, t2, t4});
            }
        }
        diskBatch.end();
        return diskBatch;
    }
    public static GLBatch makeCube(float radius) {
        GLBatch cubeBatch=new GLBatch(GL_TRIANGLES, 36);
                
        /////////////////////////////////////////////
        // Top of cube
        cubeBatch.normal(0.0f, radius, 0.0f);
        cubeBatch.multiTexCoord(0, radius, radius);
        cubeBatch.vertex(radius, radius, radius);
        
        cubeBatch.normal(0.0f, radius, 0.0f);
        cubeBatch.multiTexCoord(0, radius, 0.0f);
        cubeBatch.vertex(radius, radius, -radius);
        
        cubeBatch.normal(0.0f, radius, 0.0f);
        cubeBatch.multiTexCoord(0, 0.0f, 0.0f);
        cubeBatch.vertex(-radius, radius, -radius);
        
        cubeBatch.normal(0.0f, radius, 0.0f);
        cubeBatch.multiTexCoord(0, radius, radius);
        cubeBatch.vertex(radius, radius, radius);
        
        cubeBatch.normal(0.0f, radius, 0.0f);
        cubeBatch.multiTexCoord(0, 0.0f, 0.0f);
        cubeBatch.vertex(-radius, radius, -radius);
        
        cubeBatch.normal(0.0f, radius, 0.0f);
        cubeBatch.multiTexCoord(0, 0.0f, radius);
        cubeBatch.vertex(-radius, radius, radius);
        
        
        ////////////////////////////////////////////
        // Bottom of cube
        cubeBatch.normal(0.0f, -radius, 0.0f);
        cubeBatch.multiTexCoord(0, 0.0f, 0.0f);
        cubeBatch.vertex(-radius, -radius, -radius);
        
        cubeBatch.normal(0.0f, -radius, 0.0f);
        cubeBatch.multiTexCoord(0, radius, 0.0f);
        cubeBatch.vertex(radius, -radius, -radius);
        
        cubeBatch.normal(0.0f, -radius, 0.0f);
        cubeBatch.multiTexCoord(0, radius, radius);
        cubeBatch.vertex(radius, -radius, radius);
        
        cubeBatch.normal(0.0f, -radius, 0.0f);
        cubeBatch.multiTexCoord(0, 0.0f, radius);
        cubeBatch.vertex(-radius, -radius, radius);
        
        cubeBatch.normal(0.0f, -radius, 0.0f);
        cubeBatch.multiTexCoord(0, 0.0f, 0.0f);
        cubeBatch.vertex(-radius, -radius, -radius);
        
        cubeBatch.normal(0.0f, -radius, 0.0f);
        cubeBatch.multiTexCoord(0, radius, radius);
        cubeBatch.vertex(radius, -radius, radius);
        
        ///////////////////////////////////////////
        // Left side of cube
        cubeBatch.normal(-radius, 0.0f, 0.0f);
        cubeBatch.multiTexCoord(0, radius, radius);
        cubeBatch.vertex(-radius, radius, radius);
        
        cubeBatch.normal(-radius, 0.0f, 0.0f);
        cubeBatch.multiTexCoord(0, radius, 0.0f);
        cubeBatch.vertex(-radius, radius, -radius);
        
        cubeBatch.normal(-radius, 0.0f, 0.0f);
        cubeBatch.multiTexCoord(0, 0.0f, 0.0f);
        cubeBatch.vertex(-radius, -radius, -radius);
        
        cubeBatch.normal(-radius, 0.0f, 0.0f);
        cubeBatch.multiTexCoord(0, radius, radius);
        cubeBatch.vertex(-radius, radius, radius);
        
        cubeBatch.normal(-radius, 0.0f, 0.0f);
        cubeBatch.multiTexCoord(0, 0.0f, 0.0f);
        cubeBatch.vertex(-radius, -radius, -radius);
        
        cubeBatch.normal(-radius, 0.0f, 0.0f);
        cubeBatch.multiTexCoord(0, 0.0f, radius);
        cubeBatch.vertex(-radius, -radius, radius);
        
        // Right side of cube
        cubeBatch.normal(radius, 0.0f, 0.0f);
        cubeBatch.multiTexCoord(0, 0.0f, 0.0f);
        cubeBatch.vertex(radius, -radius, -radius);
        
        cubeBatch.normal(radius, 0.0f, 0.0f);
        cubeBatch.multiTexCoord(0, radius, 0.0f);
        cubeBatch.vertex(radius, radius, -radius);
        
        cubeBatch.normal(radius, 0.0f, 0.0f);
        cubeBatch.multiTexCoord(0, radius, radius);
        cubeBatch.vertex(radius, radius, radius);
        
        cubeBatch.normal(radius, 0.0f, 0.0f);
        cubeBatch.multiTexCoord(0, radius, radius);
        cubeBatch.vertex(radius, radius, radius);
        
        cubeBatch.normal(radius, 0.0f, 0.0f);
        cubeBatch.multiTexCoord(0, 0.0f, radius);
        cubeBatch.vertex(radius, -radius, radius);
        
        cubeBatch.normal(radius, 0.0f, 0.0f);
        cubeBatch.multiTexCoord(0, 0.0f, 0.0f);
        cubeBatch.vertex(radius, -radius, -radius);
        
        // Front and Back
        // Front
        cubeBatch.normal(0.0f, 0.0f, radius);
        cubeBatch.multiTexCoord(0, radius, 0.0f);
        cubeBatch.vertex(radius, -radius, radius);
        
        cubeBatch.normal(0.0f, 0.0f, radius);
        cubeBatch.multiTexCoord(0, radius, radius);
        cubeBatch.vertex(radius, radius, radius);
        
        cubeBatch.normal(0.0f, 0.0f, radius);
        cubeBatch.multiTexCoord(0, 0.0f, radius);
        cubeBatch.vertex(-radius, radius, radius);
        
        cubeBatch.normal(0.0f, 0.0f, radius);
        cubeBatch.multiTexCoord(0, 0.0f, radius);
        cubeBatch.vertex(-radius, radius, radius);
        
        cubeBatch.normal(0.0f, 0.0f, radius);
        cubeBatch.multiTexCoord(0, 0.0f, 0.0f);
        cubeBatch.vertex(-radius, -radius, radius);
        
        cubeBatch.normal(0.0f, 0.0f, radius);
        cubeBatch.multiTexCoord(0, radius, 0.0f);
        cubeBatch.vertex(radius, -radius, radius);
        
        // Back
        cubeBatch.normal(0.0f, 0.0f, -radius);
        cubeBatch.multiTexCoord(0, radius, 0.0f);
        cubeBatch.vertex(radius, -radius, -radius);
        
        cubeBatch.normal(0.0f, 0.0f, -radius);
        cubeBatch.multiTexCoord(0, 0.0f, 0.0f);
        cubeBatch.vertex(-radius, -radius, -radius);
        
        cubeBatch.normal(0.0f, 0.0f, -radius);
        cubeBatch.multiTexCoord(0, 0.0f, radius);
        cubeBatch.vertex(-radius, radius, -radius);
        
        cubeBatch.normal(0.0f, 0.0f, -radius);
        cubeBatch.multiTexCoord(0, 0.0f, radius);
        cubeBatch.vertex(-radius, radius, -radius);
        
        cubeBatch.normal(0.0f, 0.0f, -radius);
        cubeBatch.multiTexCoord(0, radius, radius);
        cubeBatch.vertex(radius, radius, -radius);

        cubeBatch.normal(0.0f, 0.0f, -radius);
        cubeBatch.multiTexCoord(0, radius, 0.0f);
        cubeBatch.vertex(radius, -radius, -radius);   
        cubeBatch.end();
        
        return cubeBatch;
    }
    
    public static ShortBuffer convertTrianglesIndicesToLines(List<Short> triangleIndices) {
        short[] lineVertices = new short[triangleIndices.size() * 2];
        int triangleCount = triangleIndices.size() / 3;
        int index = 0;
        for (int i = 0; i < triangleCount; i++) {
            short v1 = triangleIndices.get(i * 3 + 0);
            short v2 = triangleIndices.get(i * 3 + 1);
            short v3 = triangleIndices.get(i * 3 + 2);
            lineVertices[index++] = v1;
            lineVertices[index++] = v2;
            lineVertices[index++] = v2;
            lineVertices[index++] = v3;
            lineVertices[index++] = v3;
            lineVertices[index++] = v1;
        }
        return makeShortBuffer(lineVertices);
    }
    
    public static int[] genTextures(int num) {
        int[] textures=new int[num];
        glGenTextures(num, textures, 0);
        return textures;
    }
    
    public static void loadDrawableToTexture2D(Context context,int resId, int filter, int wrapMode) {
        loadDrawableToTexture2D(context, resId, filter, filter, wrapMode, false);
    }
    public static void loadDrawableToTexture2D(Context context,int resId, int minFilter,int magFilter,int wrapMode) {
        loadDrawableToTexture2D(context, resId, minFilter, magFilter, wrapMode, false);
    }
    
    public static void loadDrawableToTexture2D(Context context,int resId, int minFilter,int magFilter,int wrapMode,boolean useMip) {
        loadDrawableToTexture2D(context, resId, minFilter, magFilter, wrapMode, useMip, false);
    }
    
    public static void loadDrawableToTexture2D(Context context,int resId, int minFilter,int magFilter,int wrapMode,boolean useMip, boolean anisotropic) {
        Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), resId);
        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapMode);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapMode);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);
        GLUtils.texImage2D(GL_TEXTURE_2D, 0, bitmap, 0);
        if (useMip) {
            if (anisotropic && GLTools.isExtSupported("GL_EXT_texture_filter_anisotropic")) {
                float[] params=new float[1];
                glGetFloatv(GLES11Ext.GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, params, 0);
                glTexParameterf(GL_TEXTURE_2D, GLES11Ext.GL_TEXTURE_MAX_ANISOTROPY_EXT, params[0]);
            }
            glGenerateMipmap(GL_TEXTURE_2D);
        }
        bitmap.recycle();
    }
    
    public static void loadDrawableToTextureCube(Context context,int[] resIds,int[] cubes,int filter, int wrapMode) {
        loadDrawableToTextureCube(context, resIds, cubes, filter, filter, wrapMode, false, false);
    }
    public static void loadDrawableToTextureCube(Context context,int[] resIds,int[] cubes,int minFilter,int magFilter,int wrapMode) {
        loadDrawableToTextureCube(context, resIds, cubes, minFilter, magFilter, wrapMode, false, false);
    }
    public static void loadDrawableToTextureCube(Context context,int[] resIds,int[] cubes,int minFilter,int magFilter,int wrapMode,boolean useMip) {
        loadDrawableToTextureCube(context, resIds, cubes, minFilter, magFilter, wrapMode, useMip, false);
    }
    
    public static void loadDrawableToTextureCube(Context context,int[] resIds,int[] cubes,int minFilter,int magFilter,int wrapMode,boolean useMip, boolean anisotropic) {
        if (resIds.length!=cubes.length) {
            throw new RuntimeException("resIds must be correspond cubes");
        }
        Bitmap[] bitmaps=new Bitmap[resIds.length];
        for(int i=0;i<resIds.length;i++){
            bitmaps[i]=BitmapFactory.decodeResource(context.getResources(), resIds[i]);
        }
        
        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
        glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, wrapMode);
        glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, wrapMode);
        glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, minFilter);
        glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, magFilter);
        
        for(int i=0;i<bitmaps.length;i++){
            GLUtils.texImage2D(cubes[i], 0, bitmaps[i], 0);
        }
        
        if (useMip) {
            if (anisotropic && GLTools.isExtSupported("GL_EXT_texture_filter_anisotropic")) {
                float[] params=new float[1];
                glGetFloatv(GLES11Ext.GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, params, 0);
                glTexParameterf(GL_TEXTURE_CUBE_MAP, GLES11Ext.GL_TEXTURE_MAX_ANISOTROPY_EXT, params[0]);
            }
            glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
        }
        for (Bitmap bitmap : bitmaps) {
            bitmap.recycle();
        }
    }
    private static String[] Extexsions;
    
    public static boolean isExtSupported(String extName) {
        if (Extexsions==null) {
            String extensions = glGetString(GL_EXTENSIONS);
            if (TextUtils.isEmpty(extensions)) {
                return false;
            }
            Extexsions = extensions.split(" ");
        }
        int index = Arrays.binarySearch(Extexsions, 0, Extexsions.length, extName);
        return index!=-1;
    }

    public static void checkFrameBufferComplete(int status) {
        switch (status) {
            case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
                throw new RuntimeException("检查每个绑定状态");
            case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
                throw new RuntimeException("至少将一个缓冲区绑定到FBO");
            case GL_FRAMEBUFFER_UNSUPPORTED:
                throw new RuntimeException("没有窗口");
            case GL_FRAMEBUFFER_COMPLETE:
                break;
            default:
                throw new RuntimeException("FrameBuffer失效");
        }
    }
}
